﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;



using SynergyControls;
using SynergyLib.DSP;
using SynergyLib.SMath;
using SharpDX.XAudio2;
using SharpDX.Multimedia;
using SharpDX.XAPO;

namespace XAudio2Controls
{
    public abstract class XA2Panel : CustomControlBase
    {
        #region Fields
       
        #endregion Fields

        #region Properties
        internal bool ParameterEnabled { get; set; }
        internal SubmixVoice Submixer { get; set; }
        internal CheckBox OnOffCheckbox
        {
            get { return ResourceRoot.FindName("PART_OnOffCheckbox") as CheckBox; }
        }
        
        internal RadialControl RadialDial1
        {
            get { return ResourceRoot.FindName("PART_RadialDial1") as RadialControl; }
        }
        internal RadialControl RadialDial2
        {
            get { return ResourceRoot.FindName("PART_RadialDial2") as RadialControl; }
        }
        internal RadialControl RadialDial3
        {
            get { return ResourceRoot.FindName("PART_RadialDial3") as RadialControl; }
        }
        internal RadialControl RadialDial4
        {
            get { return ResourceRoot.FindName("PART_RadialDial4") as RadialControl; }
        }
        #endregion Properties

        #region Dependency Properties
        #region DialAngle1

        public static readonly DependencyProperty DialAngle1Property =
          DependencyProperty.Register("DialAngle1", typeof(float), typeof(XA2Panel), new PropertyMetadata(new PropertyChangedCallback(DialAngle1Changed)));

        private static void DialAngle1Changed(DependencyObject dependency, DependencyPropertyChangedEventArgs args)
        {

            XA2Panel instance = dependency as XA2Panel;

            if (instance != null && instance.ParameterEnabled)
            {
                if (instance.Submixer != null && instance.RadialDial1 != null)
                {
                    instance.UpdateParameters();
                }
            }
        }

        #endregion DialAngle1
        #region DialAngle2

        public static readonly DependencyProperty DialAngle2Property =
          DependencyProperty.Register("DialAngle2", typeof(float), typeof(XA2Panel), new PropertyMetadata(new PropertyChangedCallback(DialAngle2Changed)));

        private static void DialAngle2Changed(DependencyObject dependency, DependencyPropertyChangedEventArgs args)
        {
            XA2Panel instance = dependency as XA2Panel;

            if (instance != null && instance.ParameterEnabled)
            {
                if (instance.Submixer != null && instance.RadialDial2 != null)
                {
                    instance.UpdateParameters();
                }
            }
        }

        #endregion DialAngle1
        #region DialAngle3

        public static readonly DependencyProperty DialAngle3Property =
          DependencyProperty.Register("DialAngle3", typeof(float), typeof(XA2Panel), new PropertyMetadata(new PropertyChangedCallback(DialAngle3Changed)));

        private static void DialAngle3Changed(DependencyObject dependency, DependencyPropertyChangedEventArgs args)
        {
            XA2Panel instance = dependency as XA2Panel;

            if (instance != null && instance.ParameterEnabled)
            {
                if (instance.Submixer != null && instance.RadialDial3 != null)
                {
                    instance.UpdateParameters();
                }
            }
        }

        #endregion DialAngle1
        #region DialAngle4

        public static readonly DependencyProperty DialAngle4Property =
          DependencyProperty.Register("DialAngle4", typeof(float), typeof(XA2Panel), new PropertyMetadata(new PropertyChangedCallback(DialAngle4Changed)));

        private static void DialAngle4Changed(DependencyObject dependency, DependencyPropertyChangedEventArgs args)
        {
            XA2Panel instance = dependency as XA2Panel;

            if (instance != null && instance.ParameterEnabled)
            {
                if (instance.Submixer != null && instance.RadialDial4 != null)
                {
                    instance.UpdateParameters();
                }
            }
        }

        #endregion DialAngle1
        [Category("Common")]
        public float DialAngle4
        {
            get
            {
                return (float)GetValue(DialAngle4Property);
            }
            protected set
            {
                SetValue(DialAngle4Property, value);
            }
        }
        [Category("Common")]
        public float DialAngle3
        {
            get
            {
                return (float)GetValue(DialAngle3Property);
            }
            protected set
            {
                SetValue(DialAngle3Property, value);
            }
        }
        [Category("Common")]
        public float DialAngle2
        {
            get
            {
                return (float)GetValue(DialAngle2Property);
            }
            protected set
            {
                SetValue(DialAngle2Property, value);
            }
        }
        [Category("Common")]
        public float DialAngle1
        {
            get
            {
                return (float)GetValue(DialAngle1Property);
            }
            protected set
            {
                SetValue(DialAngle1Property, value);
            }
        }
        #endregion Dependency Properties

        #region Methods
        protected virtual void UpdateParameters() { }
        protected virtual bool EnableParameter()
        {
            if (Submixer != null)
            {
                return true;
            }
            return false;
        }
        protected virtual bool DisableParameter()
        {
            if (Submixer != null && ParameterEnabled)
            {
                return false;
            }
            return true;
        }
        protected virtual void CheckboxCollateral() { }

        #endregion Methods

        #region Event Handlers
        internal void RadialDial4_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            DialAngle4 = (float)RadialDial4.Angle;
        }
        internal void RadialDial3_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            DialAngle3 = (float)RadialDial3.Angle;
        }
        internal void RadialDial2_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            DialAngle2 = (float)RadialDial2.Angle;
        }
        internal void RadialDial1_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            DialAngle1 = (float)RadialDial1.Angle;
        }
        internal void OnOffCheckbox_Checked(object sender, RoutedEventArgs e)
        {
            switch (ParameterEnabled)
            {
                case false:
                    ParameterEnabled = EnableParameter();
                    break;
                case true:
                    ParameterEnabled = DisableParameter();
                    break;
            }

            CheckboxCollateral();
        }
        #endregion Event Handlers
    }

    public abstract class EffectsPanel : XA2Panel
    {
        #region Fields
        private byte[] ParamConfig = new byte[] { 1, 0, 0, 0, 0 };
        internal EffectTypes effectType;
        internal EffectDescriptor effectDescriptor;
        new internal AudioProcessor Effect;
        #endregion Fields

        #region Properties
        public bool EffectEnabled
        { 
            get 
            { 
                return ParameterEnabled; 
            } 
            protected set 
            { 
                ParameterEnabled = value; 
            }
        }        
        internal Label EffectTypeLabel
        {
            get { return ResourceRoot.FindName("PART_EffectTypeLabel") as Label; }
        }
        internal Label ParamLabel1
        {
            get { return ResourceRoot.FindName("PART_ParamLabel1") as Label; }
        }
        internal Label ParamLabel2
        {
            get { return ResourceRoot.FindName("PART_ParamLabel2") as Label; }
        }
        internal Label ParamLabel3
        {
            get { return ResourceRoot.FindName("PART_ParamLabel3") as Label; }
        }
        internal Label ParamLabel4
        {
            get { return ResourceRoot.FindName("PART_ParamLabel4") as Label; }
        }
        internal Slider IntensitySlider
        {
            get { return ResourceRoot.FindName("PART_IntensitySlider") as Slider; }
        }
        internal Button btnRemoveEffectPanel
        {
            get
            {
                return ResourceRoot.FindName("PART_btnRemoveEffectPanel") as Button;
            }
        }
        internal IEffectParams EffectParams
        {
            get;
            set;
        }
        internal Collection<EffectDescriptor> FxChain
        {
            get;
            set;
        }
        #endregion Properties

        #region Dependency Properties

        public static readonly DependencyProperty IntensitySliderValueProperty =
            DependencyProperty.Register("IntensitySliderValue", typeof(double), typeof(EffectsPanel), new PropertyMetadata(new PropertyChangedCallback(IntensitySliderValueChanged)));
        private static void IntensitySliderValueChanged(DependencyObject dependency, DependencyPropertyChangedEventArgs args)
        {
            EffectsPanel instance = dependency as EffectsPanel;

            if (instance != null && IntensitySliderValueProperty != null)
            {
                instance.UpdateIntensity();
            }
        }
        [Category("Common")]
        public double IntensitySliderValue
        {
            get { return (double)GetValue(IntensitySliderValueProperty); }
            protected set
            {
                SetValue(IntensitySliderValueProperty, value);
            }
        }

        public static readonly DependencyProperty ParamLabel1ContentProperty =
          DependencyProperty.Register("ParamLabel1Content", typeof(string), typeof(EffectsPanel), new PropertyMetadata(new PropertyChangedCallback(ParamLabel1ContentChanged)));
        private static void ParamLabel1ContentChanged(DependencyObject dependency, DependencyPropertyChangedEventArgs args)
        {
            EffectsPanel instance = dependency as EffectsPanel;

            if (instance != null && instance.ParameterEnabled)
            {
                if (instance.Submixer != null && instance.ParamLabel1 != null)
                {
                    //instance.UpdateLabelContents(args);
                    instance.ParamLabel1.Content = instance.EffectParams.GetParam1Readout();
                }
            }
        }
        public static readonly DependencyProperty ParamLabel2ContentProperty =
          DependencyProperty.Register("ParamLabel2Content", typeof(string), typeof(EffectsPanel), new PropertyMetadata(new PropertyChangedCallback(ParamLabel2ContentChanged)));
        private static void ParamLabel2ContentChanged(DependencyObject dependency, DependencyPropertyChangedEventArgs args)
        {
            EffectsPanel instance = dependency as EffectsPanel;

            if (instance != null && instance.ParameterEnabled)
            {
                if (instance.Submixer != null && instance.ParamLabel2 != null)
                {
                    //instance.UpdateLabelContents(args);
                    instance.ParamLabel2.Content = instance.EffectParams.GetParam2Readout();
                }
            }
        }
        public static readonly DependencyProperty ParamLabel3ContentProperty =
          DependencyProperty.Register("ParamLabel3Content", typeof(string), typeof(EffectsPanel), new PropertyMetadata(new PropertyChangedCallback(ParamLabel3ContentChanged)));
        private static void ParamLabel3ContentChanged(DependencyObject dependency, DependencyPropertyChangedEventArgs args)
        {
            EffectsPanel instance = dependency as EffectsPanel;

            if (instance != null && instance.ParameterEnabled)
            {
                if (instance.Submixer != null && instance.ParamLabel3 != null)
                {
                    //instance.UpdateLabelContents(args);
                    instance.ParamLabel3.Content = instance.EffectParams.GetParam3Readout();
                }
            }
        }
        public static readonly DependencyProperty ParamLabel4ContentProperty =
          DependencyProperty.Register("ParamLabel4Content", typeof(string), typeof(EffectsPanel), new PropertyMetadata(new PropertyChangedCallback(ParamLabel4ContentChanged)));
        private static void ParamLabel4ContentChanged(DependencyObject dependency, DependencyPropertyChangedEventArgs args)
        {
            EffectsPanel instance = dependency as EffectsPanel;
            if (instance != null && instance.ParameterEnabled)
            {
                if (instance.Submixer != null && instance.ParamLabel4 != null)
                {
                    instance.ParamLabel4.Content = instance.EffectParams.GetParam4Readout();
                }
            }
        }
        [Category("Common")]
        public string ParamLabel1Content
        {
            get
            {
                return (string)GetValue(ParamLabel1ContentProperty);
            }
            protected set
            {
                SetValue(ParamLabel1ContentProperty, value);
            }
        }
        [Category("Common")]
        public string ParamLabel2Content
        {
            get
            {
                return (string)GetValue(ParamLabel2ContentProperty);
            }
            protected set
            {
                SetValue(ParamLabel2ContentProperty, value);
            }
        }
        [Category("Common")]
        public string ParamLabel3Content
        {
            get
            {
                return (string)GetValue(ParamLabel3ContentProperty);
            }
            protected set
            {
                SetValue(ParamLabel3ContentProperty, value);
            }
        }
        [Category("Common")]
        public string ParamLabel4Content
        {
            get
            {
                return (string)GetValue(ParamLabel4ContentProperty);
            }
            protected set
            {
                SetValue(ParamLabel4ContentProperty, value);
            }
        }
        #endregion Dependency Properties

        #region Event Overrides

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            GetTemplateChildren();
        }

        #endregion Event Overrides

        #region Methods
        /// <summary>
        /// Determine how many Radial Controls this instance will need to use.
        /// </summary>
        /// <param name="dependency"></param>
        private void DetermineParamConfig()
        {
            if (EffectParams.ParamName1 != null)
            {
                ParamConfig[1] = 1;
            }
            if (EffectParams.ParamName2 != null)
            {
                ParamConfig[2] = 1;
            }
            if (EffectParams.ParamName3 != null)
            {
                ParamConfig[3] = 1;
            }
            if (EffectParams.ParamName4 != null)
            {
                ParamConfig[4] = 1;
            }
        }
        /// <summary>
        /// Add an effect to the FxChain and set ParameterEnabled to true
        /// if method is successful.
        /// </summary>
        /// <returns></returns>
        protected override bool EnableParameter()
        {
            if (Submixer != null || FxChain != null)
            {
                FxChain.Add(effectDescriptor);
                EffectDescriptor[] ed = new EffectDescriptor[FxChain.Count];
                for (int i = 0; i < FxChain.Count; i++)
                {
                    ed[i] = FxChain[i];
                }
                this.Submixer.SetEffectChain(ed);
                return true;
            }
            return false;
        }
        /// <summary>
        /// Removes effect from FxChain and set ParameterEnabled to false.
        /// </summary>
        /// <returns></returns>
        protected override bool DisableParameter()
        {
            if (Submixer != null && EffectEnabled && FxChain != null)
            {
                FxChain.Remove(effectDescriptor);
                if (FxChain.Count > 0)
                {
                    EffectDescriptor[] ed = new EffectDescriptor[FxChain.Count];
                    for (int i = 0; i < FxChain.Count; i++)
                    {
                        ed[i] = FxChain[i];
                    }
                    Submixer.SetEffectChain(ed);
                }
                else
                {
                    Submixer.SetEffectChain(null);
                }
                return false;
            }
            return true;
        }
        /// <summary>
        /// a return value of "null" in GetParamReadout() results in the dial
        /// not showing up on the final control at runtime.
        /// An effect with 2 values would return null for GetParam3Readout()
        /// and GetParam4Readout().
        /// </summary>
        /// 
        private void ConfigureLabels()
        {
            if (EffectTypeLabel != null)            
                EffectTypeLabel.Content = effectType.ToString();            
            if (ParamLabel1 != null)
                ParamLabel1.Content = EffectParams.ParamName1;
            if (ParamLabel2 != null)
                ParamLabel2.Content = EffectParams.ParamName2;
            if (ParamLabel3 != null)
                ParamLabel3.Content = EffectParams.ParamName3;
            if (ParamLabel4 != null)
                ParamLabel4.Content = EffectParams.ParamName4;
        }
        private void ConfigureRadialDials()
        {
            if (RadialDial1 != null)
            {
                switch (ParamConfig[1])
                {
                    case 1:
                        if (RadialDial1 != null)
                        {
                            RadialDial1.PropertyChanged += new PropertyChangedEventHandler(RadialDial1_PropertyChanged);
                        }
                        break;
                    case 0:
                        RadialDial1.Height = 0;
                        break;
                }
            }
            if (RadialDial2 != null)
            {
                switch (ParamConfig[2])
                {
                    case 1:
                        if (RadialDial2 != null)
                        {
                            RadialDial2.PropertyChanged += new PropertyChangedEventHandler(RadialDial2_PropertyChanged);
                        }
                        break;
                    case 0:
                        RadialDial2.Height = 0;
                        break;
                }
            }
            if (RadialDial3 != null)
            {
                switch (ParamConfig[3])
                {
                    case 1:
                        if (RadialDial3 != null)
                        {
                            RadialDial3.PropertyChanged += new PropertyChangedEventHandler(RadialDial3_PropertyChanged);
                        }
                        break;
                    case 0:
                        RadialDial3.Height = 0;
                        break;
                }
            }
            if (RadialDial4 != null)
            {
                switch (ParamConfig[4])
                {
                    case 1:
                        if (RadialDial4 != null)
                        {
                            RadialDial4.PropertyChanged += new PropertyChangedEventHandler(RadialDial4_PropertyChanged);
                        }
                        break;
                    case 0:
                        RadialDial4.Height = 0;
                        break;
                }
            }
        }
        private void ConfigureCheckBox()
        {
            if (OnOffCheckbox != null)            
                OnOffCheckbox.Checked += new RoutedEventHandler(OnOffCheckbox_Checked);
                OnOffCheckbox.Unchecked += OnOffCheckbox_Checked;
        }
        private void ConfigurebtnRemoveEffectPanel()
        {
            if (btnRemoveEffectPanel != null)
                btnRemoveEffectPanel.Click 
                    += new RoutedEventHandler(btnRemoveEffectPanel_Click);
        }
        private void ConfigureIntensitySlider()
        {
            if (IntensitySlider != null)
                IntensitySlider.ValueChanged
                    += new RoutedPropertyChangedEventHandler<double>(IntensitySlider_ValueChanged);
        }
        internal override void GetTemplateChildren()
        {
            DetermineParamConfig();
            ConfigureLabels();
            ConfigureRadialDials();
            ConfigureCheckBox();
            ConfigureIntensitySlider();
            ConfigurebtnRemoveEffectPanel();  
            GetSpecialChildren();
        }
        /// <summary>
        /// virtual override method to add controls other than a dial that may be needed.  (slider, combobox, etc)
        /// </summary>
        protected virtual void GetSpecialChildren() { }
        /// <summary>
        /// Disables the remove button if this effect is currently active.
        /// </summary>
        protected override void CheckboxCollateral()
        {
            if (ParameterEnabled)
            {
                btnRemoveEffectPanel.IsEnabled = false;
            }
            else
            {
                btnRemoveEffectPanel.IsEnabled = true;
            }
        }
        /// <summary>
        /// Currently not implemented.
        /// </summary>
        protected virtual void UpdateIntensity() { }

        #endregion Methods

        #region Event Handlers
        void IntensitySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            IntensitySliderValue = (int)e.NewValue;
        }
        void btnRemoveEffectPanel_Click(object sender, RoutedEventArgs e)
        {            
            if (!ParameterEnabled)
            {
                FxChain.Remove(this.effectDescriptor);
                Effect.Dispose();
                StackPanel parent = ResourceRoot.FindParent<StackPanel>();
                parent.Children.Remove(this);
            }
        }
        #endregion Event Handlers
    }

    public abstract class FiltersPanel : XA2Panel
    {
        #region Fields
        int OperationSet = 0;
        FilterParameters filterprms;
        #endregion Fields

        #region Properties
        internal bool FilterEnabled
        {
            get
            {
                return ParameterEnabled;
            }
            set
            {
                ParameterEnabled = value;
            }
        }
        public float CutOffFrequency
        {
            get
            {
                return Trig.AngleToCutoffFrequency((float)GetValue(DialAngle2Property), Submixer.VoiceDetails.InputSampleRate);
            }
        }
        public float ActualOneOverQ
        {
            get
            {
                return (float)(Math.Abs(DialAngle1) / 360);
            }
        }
        ComboBox FilterTypeComboBox
        {
            get { return ResourceRoot.FindName("PART_FilterTypeComboBox") as ComboBox; }
        }
        internal FilterParameters FilterPrms
        {
            get
            {
                return filterprms;
            }
            set
            {
                filterprms = value;
            }
        }
        Slider VolumeSlider
        {
            get { return ResourceRoot.FindName("PART_Volume") as Slider; }
        }
        #endregion Properties

        #region DependencyProperties

        #region FilterType
        public static readonly DependencyProperty FilterTypeProperty =
           DependencyProperty.Register("FilterType", typeof(FilterType), typeof(FilterPanel), new UIPropertyMetadata(FilterType.LowPassFilter, OnFilterTypeChanged, OnCoerceFilterType));

        private static object OnCoerceFilterType(DependencyObject o, object value)
        {
            FilterPanel instance = o as FilterPanel;
            if (instance != null)
                return instance.OnCoerceFilterType((FilterType)value);
            else
                return value;
        }
        protected virtual FilterType OnCoerceFilterType(FilterType value)
        {
            return value;
        }

        private static void OnFilterTypeChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            FilterPanel instance = o as FilterPanel;
            if (instance != null)
                instance.OnFilterTypeChanged((FilterType)e.OldValue, (FilterType)e.NewValue);
        }
        protected virtual FilterType OnFilterTypeChanged(FilterType oldValue, FilterType newValue)
        {
            return newValue;
        }
        #endregion FilterType

        [Category("Common")]
        public FilterType Filter
        {
            get
            {
                return (FilterType)GetValue(FilterTypeProperty);
            }
            protected set
            {
                SetValue(FilterTypeProperty, value);
            }
        }


        #endregion Dependency Properties

        #region Static Constructor
        static FiltersPanel()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(FilterPanel), new FrameworkPropertyMetadata(typeof(FilterPanel)));
        }
        #endregion

        #region Methods
        /// <summary>
        /// Apply FilterParameters that are acoustically equivalent to bypassing the filter.
        /// Sets FilterEnabled to false if new FilterParameters are applied successfully.
        /// </summary>        
        protected override bool DisableParameter()
        {
            if (Submixer != null && FilterEnabled)
            {
                FilterParameters filterBypass = new FilterParameters()
                {
                    Type = FilterType.LowPassFilter,
                    Frequency = 1.0f,
                    OneOverQ = 1.0f,
                };
                Submixer.SetFilterParameters(filterBypass, OperationSet);
                return false;
            }
            return true;
        }
        protected override void UpdateParameters()
        {
            if (ParameterEnabled)
            {
                FilterPrms = new FilterParameters()
                {
                    Type = Filter,
                    Frequency = CutOffFrequency,
                    OneOverQ = ActualOneOverQ,
                }
                        ;
                Submixer.SetFilterParameters(FilterPrms, OperationSet);
            }
        }
        private void ConfigureRadialDials()
        {
            if (RadialDial1 != null)
                RadialDial1.PropertyChanged += new PropertyChangedEventHandler(RadialDial1_PropertyChanged);            
            if (RadialDial2 != null) 
                RadialDial2.PropertyChanged += new PropertyChangedEventHandler(RadialDial2_PropertyChanged);            
        }
        private void ConfigureCheckBox()
        {
            if (OnOffCheckbox != null)            
                OnOffCheckbox.Checked += new RoutedEventHandler(OnOffCheckbox_Checked);
                OnOffCheckbox.Unchecked += OnOffCheckbox_Checked;            
        }
        private void ConfigureFilterTypeComboBox()
        {
            if (FilterTypeComboBox != null)
            {
                FilterTypeComboBox.ItemsSource = Enum.GetValues(typeof(FilterType));
                FilterTypeComboBox.SelectionChanged += FilterTypeComboBox_SelectionChanged;
                FilterTypeComboBox.SelectedIndex = 0;
            }
        }        
        private void ConfigureVolumeSlider()
        {
            if (VolumeSlider != null) 
                VolumeSlider.ValueChanged 
                    += new RoutedPropertyChangedEventHandler<double>(VolumeSlider_ValueChanged);            
        }
        internal override void GetTemplateChildren()
        {
            ConfigureFilterTypeComboBox();
            ConfigureCheckBox();
            ConfigureRadialDials();
            ConfigureVolumeSlider(); 
            GetSpecialChildren();
        }
        protected virtual void GetSpecialChildren() { }
        #endregion Methods

        #region Event Handlers

        void FilterTypeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var selection = (sender as ComboBox).SelectedValue;
            Filter = (FilterType)selection;
        }

        internal void VolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (VolumeSlider != null && Submixer != null)
            {
                Submixer.SetVolume((float)e.NewValue, 0);
            }
        }

        #endregion EventHandlers
    }
}
